package org.tcshare.utils.bluetooth;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Dialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import org.tcshare.dialog.LoadingDialog;
import org.tcshare.dialog.MsgDialogUtil;
import org.tcshare.utils.HexDump;
import org.tcshare.utils.PacketUtil;
import org.tcshare.utils.ToastUtil;

import java.nio.ByteBuffer;
import java.util.List;
import java.util.Observable;
import java.util.UUID;

public class BTUtil extends Observable {
    private static final String TAG = BTUtil.class.getSimpleName();
    private long maxWaitTime = 10_000;
    private Activity act;
    private BTSearchDialog btSearchDialog;
    private int supportedMTU = 20;
    private BluetoothGatt btGatt;
    private BluetoothGattCharacteristic writeCharacteristic;
    private BluetoothGattCharacteristic notifyCharacteristic;
    private final String servicesUUID;   //服务的UUID
    private final String notifyUUID;     // 读
    private final String writeUUID;      // 写

    private byte[] sendBytes;
    private static final Handler handler = new Handler(Looper.getMainLooper());
    private Dialog loadingDialog;

    private final Runnable dismissLoadingDialogDelay = () -> reset(maxWaitTime / 1000 + "秒内，未收到蓝牙返回的信息，请重新连接！");
    private final BluetoothAdapter bluetoothAdapter;
    private BluetoothDevice connectDev;
    private final PacketUtil packetUtil;

    public BTUtil(String servicesUUID, String notifyUUID, String writeUUID, long maxWaitTime, PacketUtil packetUtil) {
        this.servicesUUID = servicesUUID;
        this.notifyUUID = notifyUUID;
        this.writeUUID = writeUUID;
        this.maxWaitTime = maxWaitTime;
        this.packetUtil = packetUtil;
        bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

    }

    public void setActivity(Activity act) {
        this.act = act;
        loadingDialog = LoadingDialog.createLoadingDialog(act, "正在处理，请稍后...");
        btSearchDialog = new BTSearchDialog(act, bluetoothAdapter, this::connectBT);
    }

    public void reset(String msg) {
        setChanged();
        notifyObservers(new BTDisconnect());

        connectDev = null;
        handler.removeCallbacks(dismissLoadingDialogDelay);
        loadingDialog.dismiss();
        if (btGatt != null) {
            btGatt.close();
        }

        loadingDialog.dismiss();

        packetUtil.resetPacket();
        if (msg != null) {
            MsgDialogUtil.showMsg(act, msg);
        }
    }


    public void searchBT() {
        btSearchDialog.show();
    }

    protected void connectBT(BluetoothDevice device) {
        this.connectDev = device;
        loadingDialog.show();
        btGatt = device.connectGatt(act, false, new BluetoothGattCallback() {
            private final PacketUtil.PacketReadyCallBack cb = new PacketUtil.PacketReadyCallBack() {
                @Override
                public void onPacketReady(byte[] recvBytes) {
                    handler.removeCallbacks(dismissLoadingDialogDelay);
                    loadingDialog.dismiss();

                    Log.e(TAG, "recevPacket:" + HexDump.toHexString(recvBytes));
                    PacketUtil.ValidPacket ret = packetUtil.validPayload(recvBytes);

                    act.runOnUiThread(() -> {
                        setChanged();
                        notifyObservers(new BTMsg(ret.valid, ret.type, ret.payload, sendBytes, recvBytes));
                    });

                }
            };

            @Override
            public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
                super.onMtuChanged(gatt, mtu, status);
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    act.runOnUiThread(() -> ToastUtil.showToastShort(act, "mtu:" + mtu));
                    supportedMTU = mtu - 3;
                    Log.e(TAG, "onMtuChanged:" + (mtu - 3) + ":" + status);
                }
            }

            @Override
            public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
                super.onCharacteristicRead(gatt, characteristic, status);
                Log.e(TAG, "onCharacteristicRead:" + HexDump.toHexString(characteristic.getValue()));
            }

            @Override
            public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
                super.onCharacteristicWrite(gatt, characteristic, status);
                Log.e(TAG, "onCharacteristicWrite:" + HexDump.toHexString(characteristic.getValue()));
            }

            @Override
            public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
                super.onConnectionStateChange(gatt, status, newState);
                Log.e(TAG, "onConnectionStateChange:" + "-" + status + "-" + newState);
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    btGatt.discoverServices();
                    Log.e(TAG, "GATT连接成功！下一步找对应的服务。");
                } else {
                    act.runOnUiThread(() -> reset("不支持的设备，无法连接到设备！"));
                }
            }

            @SuppressLint("SetTextI18n")
            @Override
            public void onServicesDiscovered(BluetoothGatt gatt, int status) {
                super.onServicesDiscovered(gatt, status);
                Log.e(TAG, "onServicesDiscovered");
                try {
                    if (status == BluetoothGatt.GATT_SUCCESS) {
                        // 打印一下所有的服务
                        List<BluetoothGattService> supportedGattServices = btGatt.getServices();
                        for (int i = 0; i < supportedGattServices.size(); i++) {
                            Log.e("success", "1:BluetoothGattService UUID=:" + supportedGattServices.get(i).getUuid());
                            List<BluetoothGattCharacteristic> listGattCharacteristic = supportedGattServices.get(i).getCharacteristics();
                            for (int j = 0; j < listGattCharacteristic.size(); j++) {
                                int charaProp = listGattCharacteristic.get(j).getProperties();
                                String p = ((charaProp | BluetoothGattCharacteristic.PROPERTY_READ) > 0) ? "属性可读" : "属性不可读";
                                Log.e("success", "2:   BluetoothGattCharacteristic UUID=:" + listGattCharacteristic.get(j).getUuid()
                                        + "=" + p
                                        + "=" + listGattCharacteristic.get(j).getWriteType());
                            }
                        }
                    } else {
                        act.runOnUiThread(() -> {
                            loadingDialog.dismiss();
                            MsgDialogUtil.showMsg(act, "找不到服务，无法连接到设备！");
                        });
                        return;
                    }

                    //设置serviceUUID
                    BluetoothGattService bluetoothGattService = btGatt.getService(UUID.fromString(servicesUUID));
                    //设置写入特征UUID
                    writeCharacteristic = bluetoothGattService.getCharacteristic(UUID.fromString(writeUUID));
                    //设置监听特征UUID
                    notifyCharacteristic = bluetoothGattService.getCharacteristic(UUID.fromString(notifyUUID));

                    gatt.readCharacteristic(notifyCharacteristic); // 读也添加进来


                    List<BluetoothGattDescriptor> descriptors = notifyCharacteristic.getDescriptors();
                    for (BluetoothGattDescriptor descriptor : descriptors) {
                        boolean b1 = descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); // 所有的都设置一下允许通知，否则收不到信息
                        if (b1) {
                            gatt.writeDescriptor(descriptor);
                            Log.e(TAG, "描述 UUID :" + descriptor.getUuid().toString() + "设置允许通知成功！");
                        } else {
                            Log.e(TAG, "描述 UUID :" + descriptor.getUuid().toString() + " 设置允许通知失败");
                        }
                    }

                    //开启监听
                    boolean ret = gatt.setCharacteristicNotification(notifyCharacteristic, true);
                    if (!ret) {
                        act.runOnUiThread(() -> MsgDialogUtil.showMsg(act, "监听蓝牙通知失败！"));
                        return;
                    }
                    Log.e("TAG", "设置监听成功!");


                    act.runOnUiThread(() -> {
                        setChanged();
                        notifyObservers(new BTConnect(device));

                        ToastUtil.showToastShort(act, "设备连接成功！");
                        bluetoothAdapter.cancelDiscovery();
                        int mtu = 512;
                        Log.e(TAG, "request " + mtu + " mtu:" + gatt.requestMtu(mtu));
                        btSearchDialog.dismiss();
                    });
                } catch (Exception e) {
                    e.printStackTrace();
                    act.runOnUiThread(() -> MsgDialogUtil.showMsg(act, "连接失败，该程序仅支持连接指定设备！"));
                } finally {
                    loadingDialog.dismiss();
                }
            }


            @Override
            public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
                super.onCharacteristicChanged(gatt, characteristic);
                packetUtil.preparePacket(characteristic.getValue(), cb);
            }

        });

    }

    public void sendData(byte[] payload) {
        sendData(payload, true);
    }

    public void sendData(byte[] payload, boolean showProgress) {
        if (connectDev == null || writeCharacteristic == null || notifyCharacteristic == null) {
            ToastUtil.showToastShort(act, "未连接到设备！");
            return;
        } else if (payload == null || payload.length == 0) {
            ToastUtil.showToastShort(act, "发送内容为空！");
            return;
        }
        if (showProgress) {
            loadingDialog.show();
            handler.postDelayed(dismissLoadingDialogDelay, maxWaitTime);// N秒内，未收到蓝牙返回的通知，结束进度框。
        }
        sendBytes = payload;
        Log.e(TAG, "需要发送的数据：" + HexDump.toHexString(payload));
        ByteBuffer tmpBB = ByteBuffer.allocate(supportedMTU);
        for (byte b : payload) {
            tmpBB.put(b);
            if (!tmpBB.hasRemaining()) {
                byte[] packet = tmpBB.array();
                Log.e(TAG, "sendPacket:" + HexDump.toHexString(packet));
                if (sendPacketFailed(packet)) {
                    tmpBB.clear();
                    if (showProgress) {
                        MsgDialogUtil.showMsg(act, "拆分数据包发送失败，请重试！");
                    }
                    return;
                }
                tmpBB.clear();
            }
        }
        tmpBB.flip();
        byte[] packet = new byte[tmpBB.limit()];
        tmpBB.get(packet);
        if (packet.length > 0) {
            Log.e(TAG, "sendPacketFlush:" + HexDump.toHexString(packet));
            if (sendPacketFailed(packet)) {
                if (showProgress) {
                    MsgDialogUtil.showMsg(act, "拆分数据包发送失败，请重试！");
                }
            }
        }

    }

    private boolean sendPacketFailed(byte[] bytes) {
        writeCharacteristic.setValue(bytes);
//        writeCharacteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
        boolean ret = btGatt.writeCharacteristic(writeCharacteristic);
        //TODO 使用队列或timer等方式来处理
        try {
            Thread.sleep(30);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return !ret;

    }

    public BluetoothDevice getConnectDev() {
        return connectDev;
    }

    public static class BTMsg {
        public final boolean validPacket;
        public final byte[] sendBytes;
        public final byte[] recvBytes;
        public final byte[] payload;
        private final byte type;

        public BTMsg(boolean validPacket, byte type, byte[] payload, byte[] sendBytes, byte[] recvBytes) {
            this.validPacket = validPacket;
            this.type = type;
            this.sendBytes = sendBytes;
            this.recvBytes = recvBytes;
            this.payload = payload;
        }


    }

    public static class BTDisconnect {
    }

    public static class BTConnect {
        public BluetoothDevice device;

        public BTConnect(BluetoothDevice device) {
            this.device = device;
        }
    }

}
